package info.amber.jdk8.test;

import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.time.Clock;
import java.time.Instant;
import java.util.Comparator;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Abc {

	public static void main(String[] args) {
		Function<String, Integer> toInteger = Integer::valueOf;
		Function<String, String> backToString = toInteger.andThen(String::valueOf);
		backToString.apply("123"); // "123"

		//
		functionInterface();

		//
		converter_();

		funcAndContructQuote();

		//
		lambda_x();
		//
		Predicate<String> predicate = (s) -> s.length() > 0;
		predicate.test("foo");
		predicate.negate()
				.test("foo"); // false

		Predicate<Boolean> nonNull = Objects::nonNull;
		Predicate<Boolean> isNull = Objects::isNull;

		Predicate<String> isEmpty = String::isEmpty;
		Predicate<String> isNotEmpty = isEmpty.negate();
		//
		Supplier<Person> personSupplier = Person::new;
		personSupplier.get(); // new Person
		//

		Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
		greeter.accept(new Person("Luke", "Skywalker"));

		//
		Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
		Person p1 = new Person("John", "Doe");
		Person p2 = new Person("Alice", "Wonderland");

		comparator.compare(p1, p2); // > 0
		comparator.reversed()
				.compare(p1, p2); // < 0

		//

		Optional<String> optional = Optional.of("bam");
		optional.isPresent(); // true
		optional.get(); // "bam"
		optional.orElse("fallback"); // "bam"

		optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"

		//

		//
		Clock clock = Clock.systemDefaultZone();
		long millis = clock.millis();
		Instant instant = clock.instant();
		Date legacyDate = Date.from(instant); // legacy java.util.Date

		//
		anno_x();
	}

	private static void anno_x() {
		Object annos = Person.class.getAnnotations();
		System.out.println(annos);

		Hint hint = Person.class.getAnnotation(Hint.class);
		System.out.println(hint); // null
		// Hints hints1 = Person.class.getAnnotation(Hints.class);
		// System.out.println(hints1.value().length); // 2
		Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
		System.out.println(hints2.length); // 2

	}

	private static void lambda_x() {
		/* final */ int num = 1;
		Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num);
		String res = stringConverter.convert(2); // 3
		System.out.println(res);
		// num = 3;

	}

	private static void funcAndContructQuote() {
		PersonFactory<Person> personFactory = Person::new;
		Person person = personFactory.create("Peter", "Parker");

		System.out.println(person.firstName);
	}

	private static void converter_() {
		Converter<String, Integer> converter = Integer::valueOf;
		Integer converted = converter.convert("123");
		System.out.println(converted); // 123


	}

	private static void functionInterface() {

		Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
		Integer converted = converter.convert("123");
		System.out.println(converted); // 123

	}
}

@interface Hints {
	Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
	String value();
}

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

@Hints({ @Hint("hint1"), @Hint("hint2") })
// @Hint("hint1")
@Hint("hint2")
class Person {
	String	firstName;
	String	lastName;

	Person() {
	}

	Person(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}
}

interface PersonFactory<P extends Person> {
	P create(String firstName, String lastName);
}

class Lambda4 {
	static int	outerStaticNum;
	int			outerNum;

	void testScopes() {
		Converter<Integer, String> stringConverter1 = (from) -> {
			outerNum = 23;
			return String.valueOf(from);
		};

		Converter<Integer, String> stringConverter2 = (from) -> {
			outerStaticNum = 72;
			return String.valueOf(from);
		};
	}
}
